#include "maindialog.h"
#include "ui_maindialog.h"
#include "serialsettingsdialog.h"

QString WEEK_OF_DAY(int day)
{
    static const QString weekOfDay[] = {
        "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"
    };

    return weekOfDay[day % 7];
}

MainDialog::MainDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::MainDialog)
{
    ui->setupUi(this);
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));

    loadSettings();
    initLogDialog();
    initWidgets();

    m_tidOneSecond = startTimer(1000);

    m_pSerialPort = new QextSerialPort(m_portName, m_portSettings);
    connect(m_pSerialPort, SIGNAL(readyRead()), SLOT(onSerialRead()));

    m_pSerialEnumerator = new QextSerialEnumerator(this);
    m_pSerialEnumerator->setUpNotifications();
    connect(m_pSerialEnumerator, SIGNAL(deviceDiscovered(QextPortInfo)), SLOT(onSerialPortAddedOrRemoved(QextPortInfo)));
    connect(m_pSerialEnumerator, SIGNAL(deviceRemoved(QextPortInfo)), SLOT(onSerialPortAddedOrRemoved(QextPortInfo)));

    m_pTcpSoceket = new QTcpSocket(this);
    connect(m_pTcpSoceket, SIGNAL(connected()), this, SLOT(onTcpConnected()));
    connect(m_pTcpSoceket, SIGNAL(readyRead()), this, SLOT(onTcpRead()));
    connect(m_pTcpSoceket, SIGNAL(disconnected()), this, SLOT(onTcpConnectionClosedByServer()));
    connect(m_pTcpSoceket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onTcpError(QAbstractSocket::SocketError)));

    QTimer::singleShot(1, this, SLOT(onSerialOpen()));
    m_tidSendPacket = 0;

    QTimer::singleShot(5000, this, SLOT(onTcpConnect()));

    m_nTestCount = 0;
    m_testTimer = new QTimer(this);
    connect(m_testTimer, SIGNAL(timeout()), this, SLOT(onTestTimer()));
}

MainDialog::~MainDialog()
{
    if (m_tidSendPacket) {
        killTimer(m_tidSendPacket);
        m_tidSendPacket = 0;
    }
    if (m_pSerialPort->isOpen()) {
        m_pSerialPort->close();
        emit signalMsg("m_pSerialPort->close()");
    }

    exitLogDialog();
    saveSettings();

    delete ui;
}

int MainDialog::shutDownDelay()
{
    return m_shutDownDelay;
}

void MainDialog::argsMessage(const QString& message)
{
    qDebug() << message;
    if (0 == message.compare("quit")) {
        qApp->quit();
    } else {
        //emit signalNeedToShow();
    }
}

void MainDialog::keyPressEvent(QKeyEvent *e)
{
    static Qt::KeyboardModifiers _kms = (/*Qt::ControlModifier | */Qt::AltModifier);
    if ((QApplication::keyboardModifiers() & _kms) == _kms) {
        if (Qt::Key_D == e->key()) {
            m_pLogDialog->show();
        }
    }
}

void MainDialog::timerEvent(QTimerEvent *e)
{
    if (e->timerId() == m_tidSendPacket) {
        qDebug() << "m_tidSendPacket : " << e->timerId();

        killTimer(m_tidSendPacket);
        m_tidSendPacket = 0;

        int cmd = m_listSendPacket[0].dataBuf[2];
        if ((PACKET_CMD_SET == cmd) || (PACKET_CMD_GET == cmd)) {
            int bid = m_listSendPacket[0].dataBuf[3];
            int rid = m_listSendPacket[0].dataBuf[4];

            m_BoardDatas[bid].relays[rid].status = RELAY_STATUS_PENDING;
            RelayButton *button = (RelayButton *)m_pButtonGroup->button(IdButtonRelay + bid*NUM_RELAY_MAX + rid);
            button->updateStatus();
        }

        QTimer::singleShot(1, this, SLOT(onSerialSend()));
    } else if (e->timerId() == m_tidOneSecond) {
        static int _oldSecond = 0;
        QDateTime curDateTime = QDateTime::currentDateTime();

        ui->curDateTimeLabel->setText(QString("Current DateTime : %1").arg(curDateTime.toString("yyyy-MM-dd hh:mm:ss")));

        quint32 saveTime = updateSaveTime();
        quint32 saveTimeMinute = (saveTime / 60);
        m_saveKWH = m_savePerHour * 100 * saveTime;
        m_saveKWH /= (float)3600.;  // W -> WH
        m_saveKWH /= (float)1000.;  // WH -> KWH
        updatePowerServer(m_savePerHour, saveTimeMinute, m_saveKWH);

        QString text;
        text.sprintf("Power-Saving Time : %d:%02d", (saveTime/3600), (saveTimeMinute%60));
        ui->saveTimeLabel->setText(text);
        text.sprintf("Power-Saving ... : %.2f [KWH]", m_saveKWH);
        ui->saveKWHLabel->setText(text);

        if (2014 <= curDateTime.date().year()) {
            if (curDateTime.time().second() != _oldSecond) {
                _oldSecond = curDateTime.time().second();

                if (0 == _oldSecond) {
                    QTimer::singleShot(1, this, SLOT(onMinuteProc()));
                }
            }
        }
    }
}

void MainDialog::customEvent(QEvent *e)
{
    if (RelayEvent::RELAY_EVENT == e->type()) {
        RelayEvent *pEvent = (RelayEvent *)e;
        RelayData *pRelayData = (RelayData *)pEvent->data();
        int status = pEvent->action() ? RELAY_STATUS_ON : RELAY_STATUS_OFF;

        if (status != m_BoardDatas[pRelayData->bid].relays[pRelayData->id].status) {
            PacketData packetData;
            uchar bCheckSum = 0;

            packetData.sendCount = 0;
            packetData.dataBuf.push_back(PACKET_STARTEND);
            packetData.dataBuf.push_back(PACKET_MIN_LEN + 2);   // rid, status
            packetData.dataBuf.push_back(PACKET_CMD_SET);
            packetData.dataBuf.push_back(m_BoardDatas[pRelayData->bid].id);
            packetData.dataBuf.push_back(m_BoardDatas[pRelayData->bid].relays[pRelayData->id].id);
            packetData.dataBuf.push_back(status);
            for (int c=1; c<packetData.dataBuf.size(); c++) {
                bCheckSum += packetData.dataBuf[c];
            }
            packetData.dataBuf.push_back(bCheckSum);
            packetData.dataBuf.push_back(PACKET_STARTEND);

            add(packetData);
        }
    }
}

void MainDialog::loadSettings()
{
    QSettings settings(SETTINGS_FILENAME, QSettings::IniFormat);
    settings.setIniCodec("UTF-8");

    settings.beginGroup("Test");
    m_setupDateTime = settings.value("setupDateTime", "2014-04-04 12:00:00").toString();
    qDebug() << "setupDateTime : " << m_setupDateTime;
    m_savePerHour = settings.value("savePerHour", 0).toInt();
    qDebug() << "savePerHour : " << m_savePerHour;
    m_saveKWH = settings.value("saveKWH", 0).toFloat();
    qDebug() << "saveKWH : " << m_saveKWH;
    m_shutDownDelay = settings.value("shutDownDelay", 30).toInt();
    qDebug() << "shutDownDelay : " << m_shutDownDelay;
    m_testCount = settings.value("testCount", 500).toInt();
    qDebug() << "testCount : " << m_testCount;
    settings.endGroup();

    settings.beginGroup("SerialPort");
#ifdef  Q_OS_UNIX
    m_portName = settings.value("portName", "ttyO1").toString();
#else
    m_portName = settings.value("portName", "COM1").toString();
#endif
    qDebug() << "portName : " << m_portName;
    m_portSettings.BaudRate = (BaudRateType)settings.value("baudRate", BAUD9600).toInt();
    qDebug() << "baudRate : " << m_portSettings.BaudRate;
    m_portSettings.DataBits = (DataBitsType)settings.value("dataBits", DATA_8).toInt();
    qDebug() << "dataBits : " << m_portSettings.DataBits;
    m_portSettings.Parity = (ParityType)settings.value("parity", PAR_NONE).toInt();
    qDebug() << "parity : " << m_portSettings.Parity;
    m_portSettings.StopBits = (StopBitsType)settings.value("stopBits", STOP_1).toInt();
    qDebug() << "stopBits : " << m_portSettings.StopBits;
    m_portSettings.FlowControl = (FlowType)settings.value("flowControl", FLOW_OFF).toInt();
    qDebug() << "flowControl : " << m_portSettings.FlowControl;
    m_portSettings.Timeout_Millisec = 10;
    m_numRetry = settings.value("numRetry", 10).toInt();
    m_msecRetry = settings.value("msecRetry", 100).toInt();
    if (100 > m_msecRetry) {
        m_msecRetry = 100;
    }
    settings.endGroup();

    settings.beginGroup("NetworkServer");
    m_hostAddress = settings.value("hostAddress", "127.0.0.1").toString();
    qDebug() << "hostAddress : " << m_hostAddress;
    m_hostPort = settings.value("hostPort", 7500).toInt();
    qDebug() << "hostPort : " << m_hostPort;
    settings.endGroup();

    settings.beginGroup("ControllerInfo");
    m_ctrlName = settings.value("ctrlName", "cubeControl").toString();
    qDebug() << "ctrlName : " << m_ctrlName;
    for (int i=0; i<7; i++) {
        m_ctrlDayOfWeek[i] = settings.value(QString("ctrlDayOfWeek%1").arg(WEEK_OF_DAY(i)), 1).toInt();
        qDebug() << "ctrlDayOfWeek" << WEEK_OF_DAY(i) << " : " << m_ctrlDayOfWeek[i];
    }
    settings.endGroup();

    settings.beginGroup("BoardInfo");
    int numBoard = settings.value("numBoard", 1).toInt();
    qDebug() << "numBoard : " << numBoard;
    settings.endGroup();

    for (int b=0; b<numBoard; b++) {
        settings.beginGroup(QString("RelayBoard%1").arg(b));
        qDebug() << "+++Board" << b;
        BoardData boardData;

        boardData.id = b;
        qDebug() << "boardData.id : " << boardData.id;
        /*typedef struct
        {
            qint32  id;

            QString name;
            qint32  normalClose;
            qint32  numRelay;
        } BoardData;*/
        boardData.name = settings.value("name", QString::number(b)).toString();
        qDebug() << "boardData.name : " << boardData.name;
        boardData.normalClose = settings.value("normalClose", 0).toInt();
        qDebug() << "boardData.normalClose : " << boardData.normalClose;
        int numRelay = settings.value("numRelay", NUM_RELAY_DEF).toInt();
        qDebug() << "boardData.numRelay : " << numRelay;
        for (int r=0; r<numRelay; r++) {
            RelayData relayData;

            settings.beginGroup(QString("relay%1").arg(r));
            qDebug() << " +++Relay" << r;
            relayData.bid = b;
            qDebug() << " relay.bid : " << relayData.bid;
            relayData.id = r;
            qDebug() << " relay.id : " << relayData.id;
            /*typedef struct
            {
                qint32      bid;
                qint32      id;

                QString     name;
                qint32      notUsed;
                qint32      status;
                qint32      pushDelay;  // [sec]
                quint32     saveTime;   // [sec]

                QList<ShutDownData> shutDowns;
                QList<AlarmData> alarms;
            } RelayData;*/
            relayData.name = settings.value("name", QString::number(r)).toString();
            qDebug() << " relay.name : " << relayData.name;
            relayData.notUsed = settings.value("notUsed", 0).toInt();
            qDebug() << " relay.notUsed : " << relayData.notUsed;
            relayData.status = settings.value("status", RELAY_STATUS_OFF).toInt();
            qDebug() << " relay.status : " << relayData.status;
            relayData.pushDelay = settings.value("pushDelay", 0).toInt();
            qDebug() << " relay.pushDelay : " << relayData.pushDelay;
            relayData.saveTime = settings.value("saveTime", 0).toInt();
            qDebug() << " relay.saveTime : " << relayData.saveTime;

            QString shutdownData = settings.value("shutDownData", "").toString();
            qDebug() << " relay.shutdownData : " << shutdownData;
            {
                QStringList shutDownList = shutdownData.split("}", QString::SkipEmptyParts);
                for (int i=0; i<shutDownList.size(); i++) {
                    int idx = shutDownList[i].indexOf("{");
                    if (-1 < idx) {
                        QString data = shutDownList[i].mid(idx + 1);
                        QStringList dataList = data.split("||", QString::SkipEmptyParts);
                        if (2 <= dataList.size()) {
                            ShutDownData sd;
                            sd.type = 0;
                            sd.ipOrName = dataList[0];
                            sd.user = dataList[1];
                            sd.password = dataList[2];
                            relayData.shutDowns.push_back(sd);
                        }
                    }
                }
            }

            int numAlarm = settings.value("numAlarm", NUM_ALARM_DEF).toInt();
            qDebug() << " relay.numAlarm : " << numAlarm;
            for (int a=0; a<numAlarm; a++) {
                AlarmData alarmData;

                settings.beginGroup(QString("alarm%1").arg(a));
                qDebug() << "  +++Alarm" << a;
                alarmData.bid = b;
                qDebug() << "  alarm.bid : " << alarmData.bid;
                alarmData.rid = r;
                qDebug() << "  alarm.rid : " << alarmData.rid;
                alarmData.id = a;
                qDebug() << "  alarm.id : " << alarmData.id;
                /*typedef struct
                {
                    qint32  bid;
                    qint32  rid;
                    qint32  id;

                    qint32  hour;
                    qint32  minute;
                    qint32  action;
                    qint32  dayOfWeek;
                } AlarmData;*/
                alarmData.hour = settings.value("hour", -1).toInt();
                qDebug() << "  alarm.hour : " << alarmData.hour;
                alarmData.minute = settings.value("minute", -1).toInt();
                qDebug() << "  alarm.minute : " << alarmData.minute;
                alarmData.action = settings.value("action", -1).toInt();
                qDebug() << "  alarm.action : " << alarmData.action;
                alarmData.dayOfWeek = settings.value("dayOfWeek", 127).toInt();
                qDebug() << "  alarm.dayOfWeek : " << alarmData.dayOfWeek;

                qDebug() << "  ---Alarm" << a;
                settings.endGroup();

                relayData.alarms.push_back(alarmData);
            }
            qDebug() << " ---Relay" << r;
            settings.endGroup();

            boardData.relays.push_back(relayData);
        }

        m_BoardDatas.append(boardData);
        qDebug() << "---Board" << b;
        settings.endGroup();
    }
}

void MainDialog::saveSettings()
{
    QFile::remove(SETTINGS_FILENAME);
    QSettings settings(SETTINGS_FILENAME, QSettings::IniFormat);
    settings.setIniCodec("UTF-8");

    settings.beginGroup("Test");
    settings.setValue("setupDateTime", m_setupDateTime);
    settings.setValue("savePerHour", m_savePerHour);
    settings.setValue("saveKWH", m_saveKWH);
    settings.setValue("shutDownDelay", m_shutDownDelay);
    settings.setValue("testCount", m_testCount);
    settings.endGroup();

    settings.beginGroup("SerialPort");
    settings.setValue("portName", m_portName);
    settings.setValue("baudRate", m_portSettings.BaudRate);
    settings.setValue("dataBits", m_portSettings.DataBits);
    settings.setValue("parity", m_portSettings.Parity);
    settings.setValue("stopBits", m_portSettings.StopBits);
    settings.setValue("flowControl", m_portSettings.FlowControl);
    settings.setValue("numRetry", m_numRetry);
    settings.setValue("mseRcetry", m_msecRetry);
    settings.endGroup();

    settings.beginGroup("NetworkServer");
    settings.setValue("hostAddress", m_hostAddress.toString());
    settings.setValue("hostPort", m_hostPort);
    settings.endGroup();

    settings.beginGroup("ControllerInfo");
    settings.setValue("ctrlName", m_ctrlName);
    for (int i=0; i<7; i++) {
        settings.setValue(QString("ctrlDayOfWeek%1").arg(WEEK_OF_DAY(i)), m_ctrlDayOfWeek[i]);
    }
    settings.endGroup();

    settings.beginGroup("BoardInfo");
    settings.setValue("numBoard", m_BoardDatas.size());
    settings.endGroup();

    for (int b=0; b<m_BoardDatas.size(); b++) {
        settings.beginGroup(QString("RelayBoard%1").arg(b));
        BoardData boardData = m_BoardDatas.at(b);
        /*typedef struct
        {
            qint32  id;
            QString name;
            qint32  numRelay;
            qint32  normalClose;
        } BoardData;*/
        settings.setValue("id", boardData.id);
        settings.setValue("name", boardData.name);
        settings.setValue("normalClose", boardData.normalClose);

        settings.setValue("numRelay", boardData.relays.size());
        for (int r=0; r<boardData.relays.size(); r++) {
            settings.beginGroup(QString("relay%1").arg(r));
            /*typedef struct
            {
                qint32      bid;
                qint32      id;
                QString     name;
                qint32      notUsed;
                qint32      status;
                qint32      pushDelay;  // [sec]
                quint32     saveTime;   // [sec]

                QList<ShutDownData> shutDowns;
                QList<AlarmData> alarms;
            } RelayData;*/
            settings.setValue("id", boardData.relays[r].id);
            settings.setValue("name", boardData.relays[r].name);
            settings.setValue("notUsed", boardData.relays[r].notUsed);
            settings.setValue("status", boardData.relays[r].status);
            settings.setValue("pushDelay", boardData.relays[r].pushDelay);
            settings.setValue("saveTime", boardData.relays[r].saveTime);

            QString shutDownData = "";
            for (int i=0; i<boardData.relays[r].shutDowns.size(); i++) {
                shutDownData += "{";
                shutDownData += boardData.relays[r].shutDowns[i].ipOrName;
                shutDownData += "||";
                shutDownData += boardData.relays[r].shutDowns[i].user;
                shutDownData += "||";
                shutDownData += boardData.relays[r].shutDowns[i].password;
                shutDownData += "}";
            }
            settings.setValue("shutDownData", shutDownData);

            settings.setValue("numAlarm", boardData.relays[r].alarms.size());
            for (int a=0; a<boardData.relays[r].alarms.size(); a++) {
                settings.beginGroup(QString("alarm%1").arg(a));
                /*typedef struct
                {
                    qint32  hour;
                    qint32  minute;
                    qint32  action;
                    qint32  dayOfWeek;
                } AlarmData;*/
                settings.setValue("hour", boardData.relays[r].alarms[a].hour);
                settings.setValue("minute", boardData.relays[r].alarms[a].minute);
                settings.setValue("action", boardData.relays[r].alarms[a].action);
                settings.setValue("dayOfWeek", boardData.relays[r].alarms[a].dayOfWeek);
                settings.endGroup();
            }
            settings.endGroup();
        }
        settings.endGroup();
    }
}

void MainDialog::initLogDialog()
{
    QDateTime cur = QDateTime::currentDateTime();
    QString logFileName = QApplication::applicationDirPath();
    logFileName += cur.toString(QString("/log/yyyyMMdd-hhmmss.log"));

    m_pLogDialog = new LogDialog(logFileName);
    connect(this, SIGNAL(signalMsg(const QString)), m_pLogDialog, SLOT(msg(const QString)));
    connect(this, SIGNAL(signalSave(const QString)), m_pLogDialog, SLOT(save(const QString)));

    emit signalMsg("+++ Start +++");
}

void MainDialog::exitLogDialog()
{
    if (m_pLogDialog) {
        emit signalMsg("--- End ---");
        //emit signalSave(LOGMSG_FILENAME);

        delete m_pLogDialog;
    }
}

void MainDialog::initWidgets()
{
    m_pButtonGroup = new QButtonGroup;
    connect(m_pButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(onButtonClicked(int)));

    ui->serialSettingsButton->setEnabled(false);
    m_pButtonGroup->addButton(ui->serialSettingsButton, IdButtonSerialPort);
    m_pButtonGroup->addButton(ui->testButton, IdButtonTest);
    m_pButtonGroup->addButton(ui->exitButton, IdButtonExit);

    ui->setupDateTimeLabel->setText(QString("Setup DateTime : %1").arg(m_setupDateTime));
    ui->savePerHourLabel->setText(QString("Power-Saving Per Hour : %1 [W]").arg(m_savePerHour));


    for (int b=0; b<m_BoardDatas.size(); b++) {
        QHBoxLayout *relayLayout = new QHBoxLayout;

        relayLayout->addWidget(new QLabel(m_BoardDatas[b].name));
        for (int r=0; r<m_BoardDatas[b].relays.size(); r++) {
            RelayButton *button = new RelayButton(&m_BoardDatas[b].relays[r]);
            relayLayout->addWidget(button);
            m_pButtonGroup->addButton(button, (IdButtonRelay + b*NUM_RELAY_MAX + r));
        }
        ui->boardLayout->addLayout(relayLayout);
    }
}

void MainDialog::add(PacketData &packetData, bool noSend)
{
    /*QString pd(packetData.dataBuf.toHex());
    qDebug() << "add(" << pd << ")";*/

    m_listSendPacket.push_back(packetData);
    if ((1 == m_listSendPacket.size() && (false == noSend))) {
        //QTimer::singleShot(1, this, SLOT(onSerialSend()));
        onSerialSend();
    }
}

void MainDialog::initServer()
{
    // $INIT,cname,...,[bid,bname,btype<rid,rname,rnotUsed,rstatus,<...,$
    if (QAbstractSocket::ConnectedState == m_pTcpSoceket->state()) {
        QString netPacket("$INIT,");
        netPacket += QString("%1,").arg(m_ctrlName);
        {
            quint32 saveTime = updateSaveTime();
            quint32 saveTimeMinute = (saveTime / 60);
            m_saveKWH = m_savePerHour * 100 * saveTime;
            m_saveKWH /= (float)3600.;
            netPacket += QString("%1,").arg(m_savePerHour);
            netPacket += QString("%1,").arg(saveTimeMinute);
            netPacket += QString("%1,").arg(m_saveKWH);
        }
        for (int b=0; b<m_BoardDatas.size(); b++) {
            BoardData bd = m_BoardDatas.at(b);

            netPacket += "[";
            netPacket += QString("%1,").arg(bd.id);
            netPacket += QString("%1,").arg(bd.name);
            netPacket += QString("%1,").arg(bd.normalClose);

            for (int r=0; r<bd.relays.size(); r++) {
                netPacket += "<";
                netPacket += QString("%1,").arg(bd.relays[r].id);
                netPacket += QString("%1,").arg(bd.relays[r].name);
                netPacket += QString("%1,").arg(bd.relays[r].notUsed);
                netPacket += QString("%1,").arg(bd.relays[r].status);
            }
        }
        netPacket += "$";
        qDebug() << netPacket;
        m_pTcpSoceket->write(netPacket.toUtf8());
    }
}

void MainDialog::updateStatueServer(int bid, int rid, int status)
{
    // $UPDATE,bid,rid,rstatus,$
    if (QAbstractSocket::ConnectedState == m_pTcpSoceket->state()) {
        QString netPacket("$UPDATE,");
        netPacket += QString("%1,").arg(bid);
        netPacket += QString("%1,").arg(rid);
        netPacket += QString("%1,").arg(status);
        netPacket += "$";
        qDebug() << netPacket;
        m_pTcpSoceket->write(netPacket.toUtf8());
    }
}

void MainDialog::updatePowerServer(int savePerHour, quint32 saveTimeMinute, float saveKWH)
{
    static quint32 _lastMinute = 0;
    if (_lastMinute != saveTimeMinute) {
        _lastMinute = saveTimeMinute;

        // $UPDATE_POWER,savePerHour,saveTimeMinute,saveKWH,$
        if (QAbstractSocket::ConnectedState == m_pTcpSoceket->state()) {
            QString netPacket("$UPDATE_POWER,");
            netPacket += QString("%1,").arg(savePerHour);
            netPacket += QString("%1,").arg(saveTimeMinute);
            netPacket += QString("%1,").arg(saveKWH);
            netPacket += "$";
            qDebug() << netPacket;
            m_pTcpSoceket->write(netPacket.toUtf8());
        }
    }
}

quint32 MainDialog::updateSaveTime()
{
    quint32 sumSaveTime = 0;
    for (int b=0; b<m_BoardDatas.size(); b++) {
        for (int r=0; r<m_BoardDatas[b].relays.size(); r++) {
            if (RELAY_STATUS_OFF == m_BoardDatas[b].relays[r].status) {
                m_BoardDatas[b].relays[r].saveTime++;
            }
            sumSaveTime += m_BoardDatas[b].relays[r].saveTime;
        }
    }

    return sumSaveTime;
}

void MainDialog::onButtonClicked(int id)
{
    qDebug() << "onButtonClicked(" << id << ")";

    if (IdButtonSerialPort == id) {
        SerialSettingsDialog dlg(m_portName, &m_portSettings, this);
        if (QDialog::Accepted == dlg.exec()) {
            m_portName = dlg.portName();
            QTimer::singleShot(1, this, SLOT(onSerialOpen()));
        }
    } else if (IdButtonTest == id) {
        if (0 == m_nTestCount) {
            m_testTimer->start(100);
        } else {
            m_testTimer->stop();
            m_nTestCount = 0;
        }
    } else if (IdButtonExit == id) {
        qApp->quit();
    } else if ((IdButtonRelay <= id) && (IdButtonRelayMax >= id)) {
        id = id - IdButtonRelay;

        int bid = id / NUM_RELAY_MAX;
        int rid = id % NUM_RELAY_MAX;
        int status = m_BoardDatas[bid].relays[rid].status;

        if ((RELAY_STATUS_OFF == status) || (RELAY_STATUS_ON == status)) {
            if ((RELAY_STATUS_ON == status) && m_BoardDatas[bid].relays[rid].shutDowns.size()) {
                for (int i=0; i<m_BoardDatas[bid].relays[rid].shutDowns.size(); i++) {
                    ShutDownData sd = m_BoardDatas[bid].relays[rid].shutDowns.at(i);
                    QString program = QString("net rpc shutdown -S %1 -U %2%%3 &").arg(sd.ipOrName).arg(sd.user).arg(sd.password);
                    qDebug() << program;

                    QProcess process;
                    process.start(program);
                    process.waitForFinished();
                }

                m_BoardDatas[bid].relays[rid].status = RELAY_STATUS_SHUTDOWN;
                RelayButton *button = (RelayButton *)m_pButtonGroup->button(IdButtonRelay + bid*NUM_RELAY_MAX + rid);
                button->updateStatus();

                updateStatueServer(bid, rid, RELAY_STATUS_SHUTDOWN);
            } else {
                PacketData packetData;
                uchar bCheckSum = 0;

                packetData.sendCount = 0;
                packetData.dataBuf.push_back(PACKET_STARTEND);
                packetData.dataBuf.push_back(PACKET_MIN_LEN + 2);   // rid, status
                packetData.dataBuf.push_back(PACKET_CMD_SET);
                packetData.dataBuf.push_back(m_BoardDatas[bid].id);
                packetData.dataBuf.push_back(m_BoardDatas[bid].relays[rid].id);
                packetData.dataBuf.push_back((RELAY_STATUS_ON==status) ? RELAY_STATUS_OFF : RELAY_STATUS_ON);
                for (int c=1; c<packetData.dataBuf.size(); c++) {
                    bCheckSum += packetData.dataBuf[c];
                }
                packetData.dataBuf.push_back(bCheckSum);
                packetData.dataBuf.push_back(PACKET_STARTEND);

                add(packetData);
            }
        } else if (RELAY_STATUS_UNKNOWN == status) {
            PacketData packetData;
            uchar bCheckSum = 0;

            packetData.sendCount = 0;
            packetData.dataBuf.push_back(PACKET_STARTEND);
            packetData.dataBuf.push_back(PACKET_MIN_LEN + 1);   // rid
            packetData.dataBuf.push_back(PACKET_CMD_GET);
            packetData.dataBuf.push_back(m_BoardDatas[bid].id);
            packetData.dataBuf.push_back(m_BoardDatas[bid].relays[rid].id);
            for (int c=1; c<packetData.dataBuf.size(); c++) {
                bCheckSum += packetData.dataBuf[c];
            }
            packetData.dataBuf.push_back(bCheckSum);
            packetData.dataBuf.push_back(PACKET_STARTEND);

            add(packetData);
        }
    }
}

void MainDialog::onSerialOpen()
{
    if (m_tidSendPacket) {
        killTimer(m_tidSendPacket);
        m_tidSendPacket = 0;
    }
    m_recvBuf.clear();
    if (m_pSerialPort->isOpen()) {
        m_pSerialPort->close();
        emit signalMsg("m_pSerialPort->close()");
    }
    m_pSerialPort->setPortName(m_portName);
    m_pSerialPort->setQueryMode(QextSerialPort::EventDriven);
    m_pSerialPort->setBaudRate(m_portSettings.BaudRate);
    m_pSerialPort->setDataBits(m_portSettings.DataBits);
    m_pSerialPort->setParity(m_portSettings.Parity);
    m_pSerialPort->setStopBits(m_portSettings.StopBits);
    m_pSerialPort->setFlowControl(m_portSettings.FlowControl);
    m_pSerialPort->setTimeout(m_portSettings.Timeout_Millisec);
    bool ret = m_pSerialPort->open(QIODevice::ReadWrite);
    {
        QString msg = QString("%1 = m_pSerialPort->open(%2)").arg(ret).arg(m_portName);
        emit signalMsg(msg);
    }
    if (ret) {
        PacketData packetData;
        uchar bCheckSum;

        for (int b=0; b<m_BoardDatas.size(); b++) {
            packetData.sendCount = 0;
            packetData.dataBuf.clear();

            packetData.dataBuf.push_back(PACKET_STARTEND);
            packetData.dataBuf.push_back(PACKET_MIN_LEN + 2);   // numRelay, normalClose
            packetData.dataBuf.push_back((char)PACKET_CMD_BDINIT);
            packetData.dataBuf.push_back(m_BoardDatas[b].id);
            packetData.dataBuf.push_back(m_BoardDatas[b].relays.size());
            packetData.dataBuf.push_back(m_BoardDatas[b].normalClose);
            bCheckSum = 0;
            for (int c=1; c<packetData.dataBuf.size(); c++) {
                bCheckSum += packetData.dataBuf[c];
            }
            packetData.dataBuf.push_back(bCheckSum);
            packetData.dataBuf.push_back(PACKET_STARTEND);

            add(packetData, true);

            for (int r=0; r<m_BoardDatas[b].relays.size(); r++) {
                packetData.sendCount = 0;
                packetData.dataBuf.clear();

                packetData.dataBuf.push_back(PACKET_STARTEND);
                if (m_BoardDatas[b].relays[r].notUsed) {
                    packetData.dataBuf.push_back(PACKET_MIN_LEN + 2);   // rid, status
                    packetData.dataBuf.push_back(PACKET_CMD_SET);
                    packetData.dataBuf.push_back(m_BoardDatas[b].id);
                    packetData.dataBuf.push_back(m_BoardDatas[b].relays[r].id);
                    packetData.dataBuf.push_back(RELAY_STATUS_NOTUSED);
                } else {
                    packetData.dataBuf.push_back(PACKET_MIN_LEN + 1);   // rid
                    packetData.dataBuf.push_back(PACKET_CMD_GET);
                    packetData.dataBuf.push_back(m_BoardDatas[b].id);
                    packetData.dataBuf.push_back(m_BoardDatas[b].relays[r].id);
                }
                bCheckSum = 0;
                for (int c=1; c<packetData.dataBuf.size(); c++) {
                    bCheckSum += packetData.dataBuf[c];
                }
                packetData.dataBuf.push_back(bCheckSum);
                packetData.dataBuf.push_back(PACKET_STARTEND);

                add(packetData, true);
            }
        }
        QTimer::singleShot(1, this, SLOT(onSerialSend()));
    }
}

void MainDialog::onSerialSend()
{
goto_next:
    if (m_listSendPacket.size() && (0 == m_tidSendPacket)) {
        m_listSendPacket[0].sendCount++;
        if (m_numRetry < m_listSendPacket[0].sendCount) {
            {
                QString pd(m_listSendPacket[0].dataBuf.toHex());
                QString msg = QString("ERR: sendCount(%1), %2, %3").arg(m_listSendPacket[0].sendCount).arg(m_listSendPacket[0].sendTime.toString("hh:mm:ss.zzz")).arg(pd);
                emit signalMsg(msg);
            }

            int cmd = m_listSendPacket[0].dataBuf[2];
            if ((PACKET_CMD_SET == cmd) || (PACKET_CMD_GET == cmd)) {
                int bid = m_listSendPacket[0].dataBuf[3];
                int rid = m_listSendPacket[0].dataBuf[4];

                m_BoardDatas[bid].relays[rid].status = RELAY_STATUS_UNKNOWN;
                RelayButton *button = (RelayButton *)m_pButtonGroup->button(IdButtonRelay + bid*NUM_RELAY_MAX + rid);
                button->updateStatus();
            }

            m_listSendPacket.removeFirst();
            goto goto_next;
        }

        m_listSendPacket[0].sendTime = QTime::currentTime();
        m_pSerialPort->write(m_listSendPacket[0].dataBuf);
        {
            QString pd(m_listSendPacket[0].dataBuf.toHex());
            QString msg = QString("SEND: %1, %2, %3").arg(m_listSendPacket[0].sendCount).arg(m_listSendPacket[0].sendTime.toString("hh:mm:ss.zzz")).arg(pd);
            emit signalMsg(msg);
        }
        if (m_msecRetry) {
            m_tidSendPacket = startTimer(m_msecRetry);
        }
    }
}

void MainDialog::onSerialRead()
{
    if (m_pSerialPort->bytesAvailable()) {
        m_recvBuf += m_pSerialPort->readAll();

        while (PACKET_MIN_LEN <= m_recvBuf.size()) {
            // 7E LEN CMD BID ... CS 7E
            while (PACKET_STARTEND != m_recvBuf.at(0)) {
                m_recvBuf.remove(0, 1);
            }

            const int nLen = m_recvBuf.at(1);
            if (nLen <= m_recvBuf.size()) {
                if (PACKET_STARTEND == m_recvBuf.at(nLen-1)) {
                    if (m_tidSendPacket) {
                        killTimer(m_tidSendPacket);
                        m_tidSendPacket = 0;
                    }

                    uchar bCheckSum = 0;
                    for (int i=1; i<nLen-2; i++) {
                        bCheckSum += m_recvBuf.at(i);
                    }
                    if (m_recvBuf.at(nLen-2) == bCheckSum) {
                        int nCmd = m_recvBuf.at(2);
                        switch (nCmd) {
                        case PACKET_CMD_BDINIT:
                            if (m_listSendPacket.size()) {
                                int bid = m_recvBuf.at(3);
                                qDebug() << "bid:" << bid;
                                {
                                    QString pd(m_recvBuf.left(nLen).toHex());
                                    QString msg = QString("RECV: %1, %2, %3").arg(m_listSendPacket[0].sendCount).arg(m_listSendPacket[0].sendTime.toString("hh:mm:ss.zzz")).arg(pd);
                                    emit signalMsg(msg);
                                }

                                if (bid == m_listSendPacket[0].dataBuf[3]) {
                                    m_listSendPacket.removeFirst();
                                }
                            }
                            break;
                        case PACKET_CMD_SET:
                        case PACKET_CMD_GET:
                            if (m_listSendPacket.size()) {
                                int bid = m_recvBuf.at(3);
                                int rid = m_recvBuf.at(4);
                                int status = m_recvBuf.at(5);
                                qDebug() << "bid:" << bid << ", rid:" << rid << ", status:" << status;
                                {
                                    QString pd(m_recvBuf.left(nLen).toHex());
                                    QString msg = QString("RECV: %1, %2, %3").arg(m_listSendPacket[0].sendCount).arg(m_listSendPacket[0].sendTime.toString("hh:mm:ss.zzz")).arg(pd);
                                    emit signalMsg(msg);
                                }
                                m_BoardDatas[bid].relays[rid].status = status;
                                RelayButton *button = (RelayButton *)m_pButtonGroup->button(IdButtonRelay + bid*NUM_RELAY_MAX + rid);
                                button->updateStatus();

                                updateStatueServer(bid, rid, status);

                                if ((bid == (int)m_listSendPacket[0].dataBuf[3]) && (rid == (int)m_listSendPacket[0].dataBuf[4])) {
                                    m_listSendPacket.removeFirst();
                                }
                            }
                            break;
                        case PACKET_CMD_NACK:
                            if (m_listSendPacket.size()) {
                                QString pd(m_recvBuf.left(nLen).toHex());
                                QString msg = QString("ERR: PACKET_CMD_NACK, %1").arg(pd);
                                emit signalMsg(msg);

                                m_listSendPacket[0].sendCount--;
                            }
                            break;
                        default:
                        {
                            QString pd(m_recvBuf.left(nLen).toHex());
                            QString msg = QString("ERR: PACKET_CMD_UNKNOWN, %1").arg(pd);
                            emit signalMsg(msg);
                        }
                            break;
                        }

                    } else {
                        QString pd(m_recvBuf.left(nLen).toHex());
                        QString msg = QString("ERR: bCheckSum(%1 != %2), %3").arg(m_recvBuf.at(nLen-2)).arg(bCheckSum).arg(pd);
                        emit signalMsg(msg);
                    }
                }

                m_recvBuf.remove(0, nLen);
            } else {
                break;
            }
        }
        QTimer::singleShot(1, this, SLOT(onSerialSend()));
    }
}

void MainDialog::onSerialPortAddedOrRemoved(QextPortInfo portInfo)
{
    Q_UNUSED(portInfo);
    QString current = m_portName;

    foreach (QextPortInfo info, QextSerialEnumerator::getPorts()) {
        if (0 == current.compare(info.portName)) {
            QTimer::singleShot(1, this, SLOT(onTimerSerialOpen()));
            break;
        }
    }
}

void MainDialog::onTcpConnect()
{
    if (m_pTcpSoceket->isOpen()) {
        m_pTcpSoceket->abort();
    }

    m_pTcpSoceket->connectToHost(m_hostAddress, m_hostPort);
}

void MainDialog::onTcpConnected()
{
    qDebug() << "onTcpConnected() : " << m_pTcpSoceket->state();
    QString msg = QString("onTcpConnected() : %1").arg(m_pTcpSoceket->state());
    emit signalMsg(msg);

    initServer();
}

void MainDialog::onTcpRead()
{
    QByteArray byteArry;
    while (m_pTcpSoceket->bytesAvailable()) {
        byteArry += m_pTcpSoceket->readAll();
    }

    QString strBuf = QString::fromUtf8(byteArry);
    qDebug() << strBuf;

    // INIT, UPDATE, UPDATE_POWER, SET
    int firstIdx = strBuf.indexOf('$');
    int lastIdx = strBuf.lastIndexOf('$');
    if ((-1 < firstIdx) && (-1 < lastIdx)) {
        int findIdx, curIdx = firstIdx + 1;
        QString cmd;

        findIdx = strBuf.indexOf(',', curIdx);
        cmd = strBuf.mid(curIdx, (findIdx-curIdx));
        curIdx = findIdx + 1;

        if (!cmd.compare("INIT")) {
            // $INIT,cname,[bid,bname,btype<rid,rname,rnotUsed,rstatus,<...,$
        } else if (!cmd.compare("UPDATE")) {
            // $UPDATE,bId,rId,rStatus,$
        } else if (!cmd.compare("UPDATE_POWER")) {
            // $UPDATE_POWER,savePerHour,saveTimeMinute,savKWH,$
        } else if (!cmd.compare("SET")) {
            // $SET,bid,rid,rstatus,$
            int bid, rid, status;

            findIdx = strBuf.indexOf(',', curIdx);
            bid = strBuf.mid(curIdx, (findIdx-curIdx)).toInt();
            curIdx = findIdx + 1;
            qDebug() << "bid:" << bid;

            findIdx = strBuf.indexOf(',', curIdx);
            rid = strBuf.mid(curIdx, (findIdx-curIdx)).toInt();
            curIdx = findIdx + 1;
            qDebug() << "rid:" << rid;

            findIdx = strBuf.indexOf(',', curIdx);
            status = strBuf.mid(curIdx, (findIdx-curIdx)).toInt();
            curIdx = findIdx + 1;
            qDebug() << "status:" << status;

            onButtonClicked(IdButtonRelay + bid*NUM_RELAY_MAX + rid);
        }
    }
}

void MainDialog::onTcpConnectionClosedByServer()
{
    qDebug() << "onTcpConnectionClosedByServer()";
    emit signalMsg("onTcpConnectionClosedByServer()");
    m_pTcpSoceket->close();
}

void MainDialog::onTcpError(QAbstractSocket::SocketError socketError)
{
    switch (socketError) {
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
        break;
    case QAbstractSocket::ConnectionRefusedError:
        break;
    default:
        break;
    }
    qDebug() << "onTcpError() : " << m_pTcpSoceket->errorString();
    emit signalMsg("onTcpError() : " + m_pTcpSoceket->errorString());

    QTimer::singleShot(5000, this, SLOT(onTcpConnect()));
}

void MainDialog::onMinuteProc()
{
    qDebug() << "onMinuteProc()";
    QDateTime curDateTime = QDateTime::currentDateTime();
    int dayOfWeek = curDateTime.date().dayOfWeek() % 7; // 1(Monday) ~ 7(Sunday)

    if (m_ctrlDayOfWeek[dayOfWeek]) {
        for (int b=0; b<m_BoardDatas.size(); b++) {
            for (int r=0; r<m_BoardDatas[b].relays.size(); r++) {
                if (m_BoardDatas[b].relays[r].notUsed) {
                    continue;
                }

                RelayButton *button = (RelayButton *)m_pButtonGroup->button(IdButtonRelay + b*NUM_RELAY_MAX + r);
                button->updateMinute(dayOfWeek, curDateTime.time().hour(), curDateTime.time().minute());
            }
        }
    } else {
        if (0 == curDateTime.time().hour()) {
            // All Off
            for (int b=0; b<m_BoardDatas.size(); b++) {
                for (int r=0; r<m_BoardDatas[b].relays.size(); r++) {
                    if (m_BoardDatas[b].relays[r].notUsed) {
                        continue;
                    }

                    if (RELAY_STATUS_OFF != m_BoardDatas[b].relays[r].status) {
                        PacketData packetData;
                        uchar bCheckSum = 0;

                        packetData.sendCount = 0;
                        packetData.dataBuf.push_back(PACKET_STARTEND);
                        packetData.dataBuf.push_back(PACKET_MIN_LEN + 2);   // rid, status
                        packetData.dataBuf.push_back(PACKET_CMD_SET);
                        packetData.dataBuf.push_back(m_BoardDatas[b].id);
                        packetData.dataBuf.push_back(m_BoardDatas[b].relays[r].id);
                        packetData.dataBuf.push_back((char)RELAY_STATUS_OFF);
                        for (int c=1; c<packetData.dataBuf.size(); c++) {
                            bCheckSum += packetData.dataBuf[c];
                        }
                        packetData.dataBuf.push_back(bCheckSum);
                        packetData.dataBuf.push_back(PACKET_STARTEND);

                        add(packetData, true);
                    }
                }
            }
            QTimer::singleShot(1, this, SLOT(onSerialSend()));
        }
    }
}

void MainDialog::onTestTimer()
{
    if (m_pSerialPort->isOpen()) {
        m_nTestCount++;
        if (m_testCount > m_nTestCount) {
            if (m_BoardDatas.size()) {
                int id = IdButtonRelay + (m_BoardDatas[0].id*NUM_RELAY_MAX) + (m_nTestCount % m_BoardDatas[0].relays.size());
                onButtonClicked(id);
            }
        } else {
            m_testTimer->stop();
            m_nTestCount = 0;
        }
    }
}
